<template>
  <el-container
    style="height: 100%; -moz-user-select: none"
    onselectstart="return false;"
  >
    <el-scrollbar class="menu-scrollbar">
      <el-aside style="width: 200px; height: 100%">
        <el-menu class="menu-kettle-node" @open="menuOpen" @close="menuClose">
          <el-submenu
            v-for="(node, index) in baseCategories"
            :key="index"
            :index="node.id"
          >
            <template slot="title">
              <i :class="node.icon"></i>
              <span>{{ node.text }}</span>
            </template>

            <el-menu-item
              v-for="item in node.children"
              :key="item.id"
              :index="item.id"
              :data-img="baseimageFilePath + item.icon"
              :data-text="item.text"
              :data-pluginId="item.pluginId"
              @mousedown.native="nodeSelect"
              :title="item.qtip"
            >
              <img
                :src="baseimageFilePath + item.icon"
                width="20"
                height="20"
              />

              {{ item.text }}
            </el-menu-item>
          </el-submenu>
        </el-menu>
      </el-aside>
    </el-scrollbar>
    <el-main style="height: 100%; padding: 0px; overflow: hidden">
      <el-container style="height: 100%">
        <el-header class="toolbar-wap" style="height: 45px; z-index: 9">
          <tool-button
            title="保存"
            icon="save"
            left-line
            @buttonClick="toolButtonClick"
          />
        </el-header>
        <el-main style="height: 100%; padding: 0px">
          <div ref="container" id="container" class="custom-container" />
        </el-main>
      </el-container>
    </el-main>
    <div
      id="DragPreview"
      :class="selectSuccess ? ' DragPreview DragPreview-ok' : 'DragPreview'"
    >
      <img width="32" height="32" :src="imgFile" />
      <span> {{ nodeLabel }}</span>
    </div>
    <el-dialog
      :visible.sync="kettleDialog"
      center
      destroy-on-close
      :close-on-click-modal="false"
      custom-class="kettleDialog"
      :width="kettleDialogWidth"
      :fullscreen="fullscreenDialog"
    >
      <div slot="title" class="dialog-title">
        <img :src="kettleDialogIcon" width="20" height="20" />
        <span v-text="kettleDialogTitle"></span>
        <div
          class="icon-fullscreen"
          :title="fullscreenDialog ? '还原' : '最大化'"
          @click="toggleDialogSize"
        >
          <svg-icon
            slot="prefix"
            :icon-class="fullscreenDialog ? 'winmin' : 'winmax'"
          />
        </div>
      </div>
      <!-- 使用内置 component 组件，编辑弹窗 -->
      <component
        v-bind:is="dialogComponent"
        :cell="selectCell"
        @setTitle="setKettleDialogTitle"
        @close="closeKettleDialog"
        @save="editCellAction"
        :style="fullscreenDialog ? '' : 'max-height:' + kettleDialogHeight"
      />
    </el-dialog>
  </el-container>
</template>
<script>
import mxgraph from "@/mxgraph/mxgraph";
import toolButton from "@/itsoftUI/components/toolbar/toolbutton";
import TableInput from "@/views/etl/kettleDialog/TableInput";
import InsertUpdate from "@/views/etl/kettleDialog/InsertUpdate";
const {
  mxGraph,
  mxCodec,
  mxUtils,
  mxGraphModel,
  mxKeyHandler,
  mxUndoManager,
  mxEvent 
} = mxgraph;
const MxGraph = mxGraph;
const MxCodec = mxCodec;
const MxUtils = mxUtils;
const MxGraphModel = mxGraphModel;
const MxKeyHandler = mxKeyHandler;
const MxUndoManager = mxUndoManager;
const MxEvent = mxEvent;
import { apiTrans } from "@/api/etl/trans";
import { apiCommon } from "@/api/etl/common";
import { getBaseUrl } from "@/api/config";
export default {
  name: "mxgraphEdit",
  components: { toolButton, TableInput, InsertUpdate },
  props: {
    dataType: {
      type: String,
      default: ""
    },
    id: {
      type: String,
      default: ""
    }
  },
  data() {
    return {
      model: null,
      graph: null,
      isSelect: false, // 点击延时表示选中
      selectSuccess: false, //选择组件在合适的范围内
      baseCategories: [], //分类
      baseimageFilePath: "", //图片路径地址
      imgFile: "",
      nodeLabel: "",
      kettleDialog: false, //弹窗
      kettleDialogTitle: "", //标题
      kettleDialogIcon: "", //图标
      kettleDialogWidth: "450px", //窗口宽度
      kettleDialogHeight: "300px", //窗口高度
      fullscreenDialog: false, //窗口最大化
      dialogComponent: "", //当前弹窗组件
      selectCell: null //选择编辑的元素
    };
  },

  methods: {
    //创建
    createGraph() {
      let self = this;
      self.graph = new MxGraph(self.$refs.container);
      //加载默认样式表
      const baseUrl =
        process.env.NODE_ENV === "production" ? "/itbi/static/" : "/static/";
      let styleNode = MxUtils
        .load(
          baseUrl +
            "mxgraph/style/default-style.xml?_dc=" +
            new Date().getTime()
        )
        .getDocumentElement();
      let dec = new MxCodec(styleNode.ownerDocument);
      dec.decode(styleNode, this.graph.getStylesheet());
    },
    //初始化
    initGraph() {
      let self = this;
      self.graph.setConnectable(true); // 允许连线
      self.graph.setCellsEditable(false); // 不可修改
      self.graph.setMultigraph(false);
      self.graph.setTooltips(true);
      self.graph.setCellsResizable(false); //调整大小
      //从value中获取显示的内容
      self.graph.convertValueToString = cell => {
        let label = cell.getAttribute("label");
        if (label) {
          if (cell.isEdge() && cell.value.nodeName == "TransHop") {
            return '<img src="' + label + '" width="16" height="16" />';
          } else if (cell.isEdge() && cell.value.nodeName == "JobHop") {
            // var jsonArray = Ext.decode(label), label = '';
            // Ext.each(jsonArray, function(img) {
            // 	label += '<img src="' + img + '" width="16" height="16" />';
            // });
            return label;
          } else {
            return decodeURIComponent(label);
          }
        }
        return label;
      }; // convertValueToString END
      // 监听双击时的编辑
      self.graph.addListener(MxEvent.DOUBLE_CLICK, (graph, evt) => {
        const cell = evt["properties"]["cell"];
        if (!cell) return;

        // 编辑时反映射节点/线条信息
        let cellId = cell.id;
        // 判断是否点击空白区域
        if (cell instanceof Array) {
          return;
        } else {
          // 判断是节点还是线条
          if (cell.edge == true) {
            // let edgeObj = self.edgeMap[cellId];
            // self.edgeFormObj = deepCopyDataObj(edgeObj);
            // self.dialogStatus_edge = "edit";
            //self.handleOpenEdgeDialog();
          } else {
            self.editCell(cell);
            //let nodeObj = self.nodeMap[cellId];
            // Object.assign(); 此方法只会深拷贝第一层数据，比如nodeObj里的第二层paramList属于第二层数组，那么不会被深拷贝，依然会以nodeMap为数据源进行联动改变
            //self.nodeFormObj = deepCopyDataObj(nodeObj);
            //self.dialogStatus_node = "edit";
            //self.handleOpenNodeDialog();
          }
        }
      }); // end of DOUBLE_CLICK

      self.initUndoManager(); // 初始化undo/redo
      self.initKeyHandler(); // 初始化键盘事件
    }, // 初始化END
    //初始化'撤销管理器'
    initUndoManager() {
      let self = this;
      self.undoMng = new MxUndoManager();
      let listen = (sender, evt) => {
        self.undoMng.undoableEditHappened(evt.getProperty("edit"));
      };
      self.graph.getModel().addListener(MxEvent.UNDO, listen);
      self.graph.getView().addListener(MxEvent.UNDO, listen);
    },
    //删除组件
    deleteCells({ cells = [], includeEdges = false, multilevel = true }) {
      let self = this;
      if (!cells || !(cells instanceof Array)) {
        throw new Error("cells 必须是一个数组");
      }
      let tmpSet = new Set(cells);

      if (multilevel) {
        cells.forEach(cell => {
          self.findDeleteCell(cell, tmpSet);
        });
      }
      self.graph.removeCells(Array.from(tmpSet), includeEdges);
    },
    //查找要删除的组件(级联删除)
    findDeleteCell(cell, deleteSet) {
      let self = this;
      deleteSet.add(cell);
      if (cell.edges) {
        cell.edges.forEach(tmpEdge => {
          if (tmpEdge.target !== cell) {
            deleteSet.add(tmpEdge.target);
            self.findDeleteCell(tmpEdge.target, deleteSet);
          }
        });
      }
    },
    //获取需要撤销操作、重复操作的组件
    getUndoRedoCell() {
      let self = this;
      let cells = [];
      if (self.undoMng) {
        let undoIndex = self.undoMng.indexOfNextAdd - 1;

        if (self.undoMng.history[undoIndex]) {
          cells = self.undoMng.history[undoIndex].changes.map(change => {
            if (change.child) {
              return change.child;
            } else {
              return change.cell;
            }
          });
        }
      }
      return cells;
    },
    //撤销操作
    undo() {
      let self = this;

      if (!self.undoMng) {
        throw new Error("mxUndoManager 没有初始化");
      }
      //console.info("后退的Cells", self.getUndoRedoCell());
      self.undoMng.undo();
    },
    //重复操作
    redo() {
      let self = this;
      if (!self.undoMng) {
        throw new Error("mxUndoManager 没有初始化");
      }
      self.undoMng.redo();
      console.info("前进的Cells", self.getUndoRedoCell());
    },
    //初始化键盘事件响应
    initKeyHandler() {
      let self = this;
      if (!self.graph) {
        throw new Error("graph 没有初始化");
      }
      self.keyHandler = new MxKeyHandler(self.graph);
      self.keyHandler.bindControlKey(90, () => {
        // 撤销
        self.undo();
      });
      self.keyHandler.bindControlKey(89, () => {
        // 前进
        self.redo();
      });
      self.keyHandler.bindKey(46, () => {
        // 删除选中的cell
        let test = self.graph.getSelectionCells();
        self.deleteCells({
          cells: self.graph.getSelectionCells(),
          includeEdges: true
        });
      });
    },

    getGraph() {
      let self = this;
      //self.graph.getModel().beginUpdate();
      apiTrans
        .openTrans(1, "/", "tt", self.baseimageFilePath)
        .then(res => {
          //展示转换
          try {
            let xmlDocument = mxUtils.parseXml(decodeURIComponent(res.data));
            let decoder = new mxCodec(xmlDocument);
            let node = xmlDocument.documentElement;
            decoder.decode(node, self.graph.getModel());
          } catch (e) {
            console.log(e);
          } finally {
            //self.graph.getModel().endUpdate();
          }
        })
        .catch(() => {});
    },
    //选择元素
    nodeSelect(e) {
      let self = this;
      self.isSelect = true;
      let DragPreviewBox = document.getElementById("DragPreview");
      self.imgFile = e.target.getAttribute("data-img");
      self.nodeLabel = e.target.getAttribute("data-text");

      setTimeout(function() {
        if (!self.isSelect) return;
        DragPreviewBox.setAttribute(
          "data-pluginId",
          e.target.getAttribute("data-pluginId")
        );
        DragPreviewBox.setAttribute(
          "data-text",
          e.target.getAttribute("data-text")
        );
        DragPreviewBox.setAttribute(
          "data-img",
          e.target.getAttribute("data-img")
        );
        DragPreviewBox.style.display = "block";
        let x = e.offsetX;
        let y = e.clientY - 30 + DragPreviewBox.offsetHeight / 2;
        DragPreviewBox.style.left = x + "px";
        DragPreviewBox.style.top = y + "px";
        self.evMouseDown(e, DragPreviewBox);
      }, 300);
      // 鼠标松开
      document.onmouseup = function(e) {
        self.isSelect = false;
        return false;
      };
    },
    //鼠标按下(移动元素）
    evMouseDown(e, selected) {
      let mouseXY = this.funGetMouseXY(e);
      let StartX = parseInt(mouseXY.x) - selected.offsetLeft;
      let StartY = parseInt(mouseXY.y) - selected.offsetTop;
      let that = this;
      //鼠标移动
      document.onmousemove = function(e) {
        let endXY = that.funGetMouseXY(e);
        // 获取移动后的元素的坐标
        let x = endXY.x - StartX - 10;
        let y = endXY.y - StartY;
        selected.style.left = x + "px";
        selected.style.top = y + "px";
        let parentContainer = document.getElementById("container");
        let scrollY = parentContainer.scrollTop;
        let scrollX = parentContainer.scrollLeft;
        let minY = parentContainer.offsetTop - scrollY;
        let maxY = minY + parentContainer.offsetHeight;
        let minX = parentContainer.offsetLeft - scrollX;
        let maxX = minX + parentContainer.offsetWidth;
        let X = Number(
          selected.style.left.substring(0, selected.style.left.length - 2)
        );
        let Y = Number(
          selected.style.top.substring(0, selected.style.top.length - 2)
        );
        //判断移动的位置是否在合适的范围内
        that.selectSuccess = X >= minX && X <= maxX && Y >= minY && Y <= maxY;
        return false;
      };
      // 鼠标松开
      document.onmouseup = function(e) {
        selected.style.display = "none";
        document.onmousemove = null;
        document.onmouseup = null;
        if (that.selectSuccess) {
          let endXY = that.funGetMouseXY(e);
          let parentContainer = document.getElementById("container");
          that.newStep(
            that.toXml(),
            selected.getAttribute("data-pluginId"),
            selected.getAttribute("data-text"),
            endXY.x - parentContainer.offsetLeft,
            endXY.y - parentContainer.offsetTop,
            selected.getAttribute("data-img")
          );
        }
        that.isSelect = false;
        that.selectSuccess = false;
        return false;
      };
    },
    //函数用于获取鼠标的位置
    funGetMouseXY(e) {
      let x = 0,
        y = 0;
      e = e || window.event;
      if (e.pageX) {
        x = e.pageX;
        y = e.pageY;
      } else {
        x = e.clientX + document.body.scrollLeft - document.body.clientLeft;
        y = e.clientY + document.body.scrollTop - document.body.clientTop;
      }
      return {
        x: x,
        y: y
      };
    },
    //加载菜单明细数据
    loadMenuNode() {
      let self = this;
      apiCommon
        .getMenuNode(self.dataType)
        .then(res => {
          self.baseCategories = res.data;
        })
        .catch(() => {});
    },
    //菜单打开
    menuOpen(index) {
      this.baseCategories.filter(item => item.id == index)[0]["icon"] =
        "el-icon-folder-opened";
    },
    //菜单收起
    menuClose(index) {
      this.baseCategories.filter(item => item.id == index)[0]["icon"] =
        "el-icon-folder";
    },
    toXml() {
      let enc = new mxCodec(mxUtils.createXmlDocument());
      let node = enc.encode(this.graph.getModel());
      return mxUtils.getPrettyXml(node);
    },
    //新转换步骤
    newStep(graphXml, pluginId, name, x, y, img) {
      let self = this;
      self.graph.getModel().beginUpdate();
      let w = 37;
      let h = 37;
      apiTrans
        .newStep(graphXml, pluginId, name)
        .then(res => {
          try {
            let doc = mxUtils.parseXml(decodeURIComponent(res.data));
            console.log(doc.documentElement);
            let cell = self.graph.insertVertex(
              self.graph.getDefaultParent(),
              null,
              doc.documentElement,
              x,
              y,
              w,
              h,
              "icon;image=" + img
            );
            self.graph.setSelectionCells([cell]);
          } catch (e) {
            console.log(e);
          } finally {
            self.graph.getModel().endUpdate();
          }
          self.graph.container.focus();
        })
        .catch(() => {});
    },
    //功能按钮
    toolButtonClick(commond) {
      switch (commond) {
        case "保存":
          this.funSave();
          break;
        default:
      }
    },
    //保存
    funSave() {
      this.$store.dispatch("app/openLoading", "正在保存转换...");
      let graphXml = encodeURIComponent(this.toXml());
      let apiSave = apiTrans.save;
      let transRepositoryId = 1;
      apiSave(transRepositoryId, graphXml)
        .then(res => {
          this.$message.success("保存成功");
          this.$store.dispatch("app/closeLoading");
        })
        .catch(() => {
          this.$store.dispatch("app/closeLoading");
        });
    },
    //编辑
    editCell(cell) {
      let self = this;
      //判断组件是否注册
      let cellType = cell.getAttribute("ctype");
      let comp = self.$options.components[cellType];
      if (!comp) return;
      //初始化尺寸
      self.kettleDialogWidth = comp.width ? comp.width : "auto";
      self.kettleDialogHeight = comp.height ? comp.height : "auto";
      //cell.getAttribute("label")
      console.log(self.kettleDialogWidth);
      self.selectCell = cell;
      self.dialogComponent = cellType;
      self.kettleDialog = true;
    },
    //编辑执行，从弹窗返回编辑数据
    editCellAction(cellData) {
      let self = this;
      let cellValue=this.selectCell.value;
      self.graph.getModel().beginUpdate();
      try {
        for (let name in cellData) {
          //设置
           cellValue.setAttribute(name,cellData[name])
        }
        this.selectCell.setValue(cellValue)
        self.graph.refresh() //刷新
      } finally {
        self.graph.getModel().endUpdate();
      }
      //保存完成关闭弹窗--
      self.closeKettleDialog();
    },
    //设置标图
    setKettleDialogTitle(title, icon) {
      this.kettleDialogTitle = title;
      this.kettleDialogIcon = icon;
    },
    //关闭弹框
    closeKettleDialog() {
      this.kettleDialog = false;
    },
    //切换窗口尺寸
    toggleDialogSize() {
      this.fullscreenDialog = !this.fullscreenDialog;
    }
  },
  mounted() {
    let self = this;
    self.baseimageFilePath = getBaseUrl() + "mxgraph/";
    console.log(self.baseimageFilePath);
    //加载菜单
    self.loadMenuNode();
    self.createGraph();
    //初始化
    self.initGraph();
    //获取数据
    self.getGraph();
  }
};
</script>

<style scoped>
.custom-container {
  height: 100%;
  width: 100%;
  background-color: #ffffff;
  position: relative;
  background-image: url("../assets/mxgraph/images/grid.gif");
}
/*选中样式*/
.DragPreview {
  display: inline;
  border: solid 2px #3089dc !important;
  position: absolute;
  padding: 0 10px;
  display: none;
  cursor: move;
  background-color: #ffffff;
}
.DragPreview-ok {
  border: solid 2px #059411 !important;
}
</style>
<style lang="scss">
.menu-kettle-node {
  .el-submenu__title {
    height: 40px;
    line-height: 40px;
  }
  .el-menu-item {
    font-size: 13px;
    height: 30px;
    line-height: 30px;
    padding: 0 25px;
    cursor: default;
  }
  .is-opened {
    .el-submenu__title {
      font-weight: bold;
    }
    .el-icon-folder-opened {
      color: #409eff;
    }
  }
  .el-menu-item.is-active {
    color: #303133;
  }
}
.menu-scrollbar {
  .el-scrollbar__wrap {
    overflow-x: hidden;
  }
}
</style>
